Perl MooseX::Method::Signatures внедряет пользовательский код во все методы

Я пытаюсь использовать MooseX::Method::Signatures и MooseX::Declare в приложении, мне нужно внедрить пользовательский код в начале каждого метода во время компиляции, а не во время выполнения:

вместо этого:

use MooseX::Declare;

method check ($value) {
     $return $value;
}

Я хочу внедрить код в начале каждого метода во время компиляции, чтобы быть таким:

method check ($value) {
     my ($value) = $self->validate($value);
     $return $value;
}

теперь я хочу код

my ($value) = $self->validate($value);

быть введенным автоматически в начале всех методов в пакете, использующих модуль MooseX:: Decalre во время компиляции, а не во время выполнения, я имею в виду не использовать модификаторы метода Moose до, после, вокруг и т. д.

Это требует модификации этих модулей, но мне нужен кто-то, кто скажет мне, с чего начать.

Я смог изменить модуль Method::Signatures::Simple, чтобы сделать это точно, и отправил письмо автору для модификации, но не получил ответа. Причина, по которой я не могу использовать это даже с модификацией, потому что она не поддерживает проверку типов и значения по умолчанию, такие как MooseX::Declare.

Модифицированная версия модуля Method::Signatures::Simple приведена ниже для справки, и я использую ее следующим образом:

используйте Method::Signatures::Simple (method => 'method,action', function => 'function', invocant=>'$this', 'inject'=>'my ($me) = $this->me;');

теперь во всех методах я получаю код my ($me) = $this->me; впрыскивается, и я просто могу использовать это так:

method check ($value) {
     say $me 
}

Вот модифицированный метод Method::Signatures::Simple.

package Method::Signatures::Simple;
{
  $Method::Signatures::Simple::VERSION = '1.07';
}

use warnings;
use strict;

=head1 NAME

Method::Signatures::Simple - Basic method declarations with signatures, without source filters

=head1 VERSION

version 1.07

=cut

use base 'Devel::Declare::MethodInstaller::Simple';

our $inject_code;

sub import {
    my $class = shift;
    my %opts  = @_;
    $opts{into} ||= caller;

    my $meth = delete $opts{name} || delete $opts{method};
    my $func = delete $opts{function};
    my $invocant = delete $opts{invocant} || '$self';
    $inject_code = delete $opts{inject};

    $inject_code .= ";" if ($inject_code && $inject_code !~ /\;$/);

    # if no options are provided at all, then we supply defaults
    unless (defined $meth || defined $func) {
        $meth = 'method';
        $func = 'func';
    }

    my @meth = split /\s*\,+\s*/, $meth;

    # we only install keywords that are requested
    foreach $meth (@meth) {
        if (defined $meth) {
            $class->install_methodhandler(
            name     => $meth,
            invocant => $invocant,
            %opts,
            );
        }
    }

    if (defined $func) {
        $class->install_methodhandler(
          name     => $func,
          %opts,
          invocant => undef,
        );
    }
}

sub strip_proto {
    my $self = shift;
    my ($proto) = $self->SUPER::strip_proto()
      or return '';
    # we strip comments and newlines here, and stash the number of newlines.
    # we will re-inject the newlines in strip_attrs(), because DD does not
    # like it when you inject them into the following code block. it does not
    # object to tacking on newlines to the code attribute spec though.
    # (see the call to inject_if_block() in DD::MethodInstaller::Simple->parser)
    $proto =~ s/\s*#.*$//mg;
    $self->{__nls} = $proto =~ s/[\r\n]//g;
    $proto;
}

sub strip_attrs {
    my $self = shift;
    my ($attrs) = $self->SUPER::strip_attrs();
    $attrs ||= '';
    $attrs .= $/ x $self->{__nls} if $self->{__nls};
    $attrs;
}

sub parse_proto {
    my $self = shift;
    my ($proto) = @_;
    $proto ||= '';
    $proto =~ s/\s*#.*$//mg;
    $proto =~ s/^\s+//mg;
    $proto =~ s/\s+$//mg;
    $proto =~ s/[\r\n]//g;
    my $invocant = $self->{invocant};

    $invocant = $1 if $proto =~ s{(\$\w+)\s*:\s*}{};

    my $inject = '';
    $inject .= "my ${invocant} = shift;" if $invocant;
    $inject .= "my ($proto) = \@_;"      if defined $proto and length $proto;
    $inject .= "$inject_code" if $inject_code;
    $inject .= '();'; # fix for empty method body

    return $inject;
}

1 ответ

Решение

Moops и Kavorka предоставляют синтаксис, практически совместимый с MooseX:: Declare и MooseX:: Method:: Signatures, и предназначены для очень расширяемого (даже изнутри!) Характера. Обращу ваше внимание на следующий раздел документации для MooseX:: Declare:

Предупреждение: MooseX:: Declare основан на Devel::Declare, гигантской сумке взлома, изначально реализованной mst с целью огорчить разработчиков Perl-ядра тем, что они сами реализовали правильную обработку ключевых слов в ядре.

[...]

Если вы хотите использовать декларативный синтаксис в новом коде, пожалуйста, ради любви к котятам, приобретите свежую версию Perl и вместо этого посмотрите на Moops.

MooseX:: Объявить себя не очень легко расширить. Я знаю. Я пробовал

Поэтому, учитывая все это, а также потому, что я написал Moops, я буду использовать это в качестве примера. Здесь мы определяем роль Kavorka::TraitFor::Sub::ProvidesMe который вставит немного кода в метод. Затем мы применяем эту роль к методу, используя does ProvideMe,

package main;
use Moops;

role Kavorka::TraitFor::Sub::ProvideMe
{
    around inject_prelude (@_)
    {
        my $prelude = $self->$next(@_);
        $prelude .= 'my ($me) = $self->me;();';
        return $prelude;
    }
}

class MyClass
{
    method me () { "tobyink" }

    method example () does ProvideMe
    {
        # This gets injected: my ($me) = $self->me;
        return $me;
    }
}

my $obj = MyClass->new;
say $obj->example;  ## says "tobyink"
Другие вопросы по тегам