这是一个很大的问题,所以请耐心等待。最后有一罐金子。
由于大多数实验原因,我正在尝试制作一个MooseX :: Declare的自定义扩展,它可以做一些对特定爱好项目有用的额外魔法。例如,我想让class
关键字注入一些额外的东西,比如从List :: Util等导入有用的实用程序,打开各种额外的编译指示(除了strict
和{{1 }},自动导入我的全局配置对象,依此类推。
所以我编写了以下测试并开始考虑是否可以让它工作。令人惊讶的是,我能够获得99%的方式,但现在我遇到了一个我无法弄清楚的问题。我的自定义warnings
关键字因注入代码中的语法错误而死亡。
class
#!/usr/bin/env perl
use MyApp::Setup;
class Foo {
use Test::More tests => 1;
has beer => ( is => 'ro', default => 'delicious' );
method something {
is $self->beer, 'delicious';
}
}
Foo->new->something;
如下所示。将来它会做更多的东西,但是现在它只是在我的MX :: D子类上调用MyApp::Setup
:
import
那个班级看起来像这样:
package MyApp::Setup;
use strict;
use warnings;
use MyApp::MooseX::Declare;
sub import {
goto &MyApp::MooseX::Declare::import;
}
1;
我设置了三个关键字类,只是包含一个替换package MyApp::MooseX::Declare;
use Moose;
use MyApp::MooseX::Declare::Syntax::Keyword::Class;
use MyApp::MooseX::Declare::Syntax::Keyword::Role;
use MyApp::MooseX::Declare::Syntax::Keyword::Namespace;
extends 'MooseX::Declare';
sub import {
my ($class, %args) = @_;
my $caller = caller;
for my $keyword ( __PACKAGE__->keywords ) {
warn sprintf 'setting up keyword %s', $keyword->identifier;
$keyword->setup_for($caller, %args, provided_by => __PACKAGE__ );
}
}
sub keywords {
# override the 'class' keyword with our own
return
( MyApp::MooseX::Declare::Syntax::Keyword::Class->new( identifier => 'class' ),
MyApp::MooseX::Declare::Syntax::Keyword::Role->new( identifier => 'role' ),
MyApp::MooseX::Declare::Syntax::Keyword::Namespace->new( identifier => 'namespace' ) );
}
1;
的额外角色。
MX::D::Syntax::NamespaceHandling
(另外两个是相同的。)
在真正的MX :: D中,NamespaceHandling内容由一个名为MooseSetup的独立角色组成,该角色本身由关键字class组成。在一个地方做这一切似乎有效;不过,我不知道结构上的轻微偏差是否是我问题的根源。有一次,我有自己的MooseSetup版本,但这导致了我无法弄清楚的构图冲突。
最后,肉和土豆是我的NamespaceHandling版本,它覆盖了package MyApp::MooseX::Declare::Syntax::Keyword::Class;
use Moose;
extends 'MooseX::Declare::Syntax::Keyword::Class';
with 'MyApp::MooseX::Declare::Syntax::NamespaceHandling';
1;
方法。其中大部分只是从原始版本中复制粘贴。
parse
当我运行测试时,一切似乎都很好 - 我收到警告消息,指出正在调用正确的方法并且正在设置包“Foo”。然后它死了:
t / default.t第5行的语法错误,靠近“{package Foo”
所以似乎某些东西在导致语法错误的package MyApp::MooseX::Declare::Syntax::NamespaceHandling;
use Moose::Role;
use Carp 'croak';
use Moose::Util 'does_role';
use MooseX::Declare::Util 'outer_stack_peek';
with 'MooseX::Declare::Syntax::NamespaceHandling';
# this is where the meat is!
sub parse {
my ($self, $ctx) = @_;
# keyword comes first
$ctx->skip_declarator;
# read the name and unwrap the options
$self->parse_specification($ctx);
my $name = $ctx->namespace;
my ($package, $anon);
# we have a name in the declaration, which will be used as package name
if (defined $name) {
$package = $name;
# there is an outer namespace stack item, meaning we namespace below
# it, if the name starts with ::
if (my $outer = outer_stack_peek $ctx->caller_file) {
$package = $outer . $package
if $name =~ /^::/;
}
}
# no name, no options, no block. Probably { class => 'foo' }
elsif (not(keys %{ $ctx->options }) and $ctx->peek_next_char ne '{') {
return;
}
# we have options and/or a block, but not name
else {
$anon = $self->make_anon_metaclass
or croak sprintf 'Unable to create an anonymized %s namespace', $self->identifier;
$package = $anon->name;
}
warn "setting up package [$package]";
# namespace and mx:d initialisations
$ctx->add_preamble_code_parts(
"package ${package}",
sprintf(
"use %s %s => '%s', file => __FILE__, stack => [ %s ]",
$ctx->provided_by,
outer_package => $package,
$self->generate_inline_stack($ctx),
),
);
# handle imports and setup here (TODO)
# allow consumer to provide specialisations
$self->add_namespace_customizations($ctx, $package);
# make options a separate step
$self->add_optional_customizations($ctx, $package);
# finish off preamble with a namespace cleanup
# we'll use namespace::sweep instead
#$ctx->add_preamble_code_parts(
# $ctx->options->{is}->{dirty}
# ? 'use namespace::clean -except => [qw( meta )]'
# : 'use namespace::autoclean'
#);
# clean up our stack afterwards, if there was a name
$ctx->add_cleanup_code_parts(
['BEGIN',
'MooseX::Declare::Util::outer_stack_pop __FILE__',
],
);
# actual code injection
$ctx->inject_code_parts(
missing_block_handler => sub { $self->handle_missing_block(@_) },
);
# a last chance to change things
$self->handle_post_parsing($ctx, $package, defined($name) ? $name : $anon);
}
1;
声明之前或之后注入了一些代码,但我无法弄清楚是什么。我已经尝试随机玩package
子中的各种项目(我实际上并不知道他们在这一点上做了什么)但我似乎无法消除甚至改变错误。当然,没有办法(我知道)实际检查生成的代码,这可能会产生线索。
感谢您的帮助。
一些更新:在浏览MooseX :: Declare :: Context之后,我添加了一些parse
语句,以确切了解通过调用print
注入的内容。这是生成(整理)的实际代码:
inject_code_parts
我不能说我知道所做的一切(特别是 package Foo;
use MyApp::MooseX::Declare outer_package => 'Foo', file => __FILE__, stack => [
MooseX::Declare::StackItem->new(q(identifier), q(class), q(handler),
q(MyApp::MooseX::Declare::Syntax::Keyword::Class), q(is_dirty), q(0),
q(is_parameterized), q(0), q(namespace), q(Foo)) ];;
BEGIN { Devel::Declare::Context::Simple->inject_scope('BEGIN {
MooseX::Declare::Util::outer_stack_pop __FILE__ }') }; ;
的事情),但这对我来说在语法上都很好。我仍然认为在所有这些之前注入代码会导致语法错误。
答案 0 :(得分:1)
嗯,这是一个调试会议的地狱,但我终于找到了问题并弄明白了。在打开MooseX::Declare::Context
和Devel::Declare::Context::Simple
(前任代理人)之后,我能够跟踪流程并通过大量转储到STDOUT,我意识到MooseSetup.pm中的一些额外处理程序,我以为我已经正确地编写了我的关键字类,实际上并没有。因此注入的结果代码没有附加正确的阴影/清理内容。
无论如何,我现在有一个看似完全正常工作的定制MooseX :: Declare!我真的很兴奋 - 这意味着我可以输入
use MyApp::Setup;
class MyApp::Foo { ... }
并且一个class
语句设置了一大堆特定于应用程序的样板。弧度。