3

我正在开发一个序列化工具,使用 Moose 来读取和写入符合非标准格式的文件。现在,我根据类中对象的默认值确定如何加载下一项,但这有其自身的缺点。相反,我希望能够使用属性元类中的信息来生成正确类型的新值。我怀疑有一种方法可以确定“isa”限制是什么并从中派生一个生成器,但我在 Moose::Meta::Attribute 或 Class::MOP::Attribute 中没有看到可以帮助我的特定方法。

这是一个更进一步的例子。假设我有以下课程:

package Example;
use Moose;

use My::Trait::Order;
use My::Class;

with 'My::Role::Load', 'My::Role::Save';

has 'foo' => (
    traits => [ 'Order' ],
    isa => 'Num',
    is => 'rw',
    default => 0,
    order => 1,
);

has 'bar' => (
    traits => [ 'Order' ],
    isa => 'ArrayRef[Str]',
    is => 'rw',
    default => sub { [ map { "" } 1..8 ] }
    order => 2,
);

has 'baz' => (
    traits => [ 'Order' ],
    isa => 'Custom::Class',
    is => 'rw',
    default => sub { Custom::Class->new() },
    order => 3,
);

__PACKAGE__->meta->make_immutable;
1;

(进一步解释:My::Role::LoadMy::Role::Save为此文件类型实现序列化角色。它们遍历它们所附加到的类的属性,并查看属性类以获取序列化顺序。)

My::Role::Load角色中,我可以遍历该类的元对象,查看所有可用的属性,并仅选择那些具有我的 Order 特征的属性:

package My::Role::Load;
use Moose;    

...

sub load {
    my ($self, $path) = @_;

    foreach my $attribute ( $self->meta->get_all_attributes ) {
        if (does_role($attribute, 'My::Trait::Order') ) {
            $self->load_attribute($attribute) # do the loading
        }
    }
}

现在,我需要知道isa元属性所代表的属性。现在,我通过获取它的一个实例来测试它,并用类似这样的东西对其进行测试:

use 5.010_001; # need smartmatch fix.
...
sub load_attribute {
    my ($self, $attribute, $fh) = @_;
    my $value = $attribute->get_value($self); # <-- ERROR PRONE PROBLEM HERE!
    if (ref($value) && ! blessed($value)) { # get the arrayref types.
        given (ref($value)) {
            when('ARRAY') { 
                $self->load_array($attribute);
            }
            when('HASH') {
                $self->load_hash($attribute);
            }
            default {
                confess "unable to serialize ref of type '$_'";
            }
        }
    }
    else {
        when (\&blessed) {
            confess "don't know how to load it if it doesn't 'load'."
                if ! $_->can('load');
            $_->load();
        }
        default {
            $attribute->set_value($self, <$fh>);
        }
    }
}

但是,正如您在 中看到的# <-- ERROR PRONE PROBLEM HERE!,整个过程依赖于属性中的一个值!如果值为 undef,我不知道要加载什么。我想$attribute->get_value($self)用一种方法来获取有关需要加载的值类型的信息。我的问题是,我在上面链接到的文档Class::MOP::Attribute似乎Moose::Meta::Attribute没有任何方法可以获取属性应该获取的对象类型。

属性的类型信息基本上就是我想要得到的。

(给未来的读者注意:这里的答案让我开始了,但它本身并不是最终的解决方案。你必须深入研究Moose::Meta::TypeConstraint课程才能真正做到我在这里寻找的东西。)

4

3 回答 3

3

不确定我是否遵循你所追求的,也许强制可以做你想做的事?

但是要获取属性isa

{
    package Foo;
    use Moose;

    has 'bar' => ( isa => 'Str', is => 'rw' );
}


my $foo = Foo->new;

say $foo->meta->get_attribute('bar')->type_constraint;   # => 'Str'

/I3az/

于 2009-12-30T19:06:48.207 回答
2

出于好奇,为什么不使用/扩展MooseX::Storage?它进行序列化,并且有大约两年半的时间。至少 MooseX::Storage 将通过展示如何编写 Moose 的(经过良好测试和生产就绪的)序列化引擎来帮助您。

于 2009-12-30T23:34:33.147 回答
1

我不太确定我是否理解(也许您可以包含一些伪代码来演示您正在寻找的内容),但听起来您可以通过定义一个新的属性特征来获得您想要的行为:设置您的属性,以便类上的一堆方法委托给属性的对象(isa => 'MySerializer', handles => [ qw(methods) ])。

您可能还需要继承Moose::Meta::Class的子类(或者更好的是,为其添加一个角色),以增强add_attribute().

编辑:如果您查看Moose::Meta::Attribute的源(特别是_process_options方法),您将看到该isa选项由Moose::Util::TypeConstraints 处理以返回要存储在type_constraint字段中的实际类型物体。这将是一个Moose::Meta::TypeConstraint::Class对象,您可以is_a_type_of()针对它进行调用。

该字段可通过type_constraintMoose::Meta::Attribute 中的方法获得。请参阅Moose::Meta::TypeConstraint了解所有可用于检查属性类型的接口。

于 2009-12-30T18:28:22.157 回答