如何使用 Moose 从属性的元对象创建值实例?

发布于 2024-08-16 13:24:14 字数 2845 浏览 5 评论 0原文

我正在开发一个序列化工具,使用 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::AttributeMoose::Meta::Attribute 的文档似乎没有任何办法获取属性应该获取的对象类型。

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

(未来的读者请注意:这里的答案让我开始,但本身并不是最终的解决方案。您必须深入研究 Moose::Meta::TypeConstraint 类才能真正执行我的操作正在找这里。)

I'm working on a serialization tool using Moose to read and write a file that conforms to a nonstandard format. Right now, I determine how to load the next item based on the default values for the objects in the class, but that has its own drawbacks. Instead, I'd like to be able to use information in the attribute meta-class to generate a new value of the right type. I suspect that there's a way to determine what the 'isa' restriction is and derive a generator from it, but I saw no particular methods in Moose::Meta::Attribute or Class::MOP::Attribute that could help me.

Here's a bit further of an example. Let's say I have the following class:

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;

(Further explanation: My::Role::Load and My::Role::Save implement the serialization roles for this file type. They iterate over the attributes of the class they're attached to, and look at the attribute classes for an order to serialize in.)

In the My::Role::Load role, I can iterate over the meta object for the class, looking at all the attributes available to me, and picking only those that have my Order trait:

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
        }
    }
}

Now, I need to know the isa of the attribute that the meta-attribute represents. Right now, I test that by getting an instance of it, and testing it with something that's kind of like this:

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>);
        }
    }
}

But, as you can see at # <-- ERROR PRONE PROBLEM HERE!, this whole process relies on there being a value in the attribute to begin with! If the value is undef, I have no indication as to what to load. I'd like to replace the $attribute->get_value($self) with a way to get information about the type of value that needs to be loaded instead. My problem is that the docs I linked to above for the Class::MOP::Attribute and the Moose::Meta::Attribute don't seem to have any way of getting at the type of object that the attribute is supposed to get.

The type information for an attribute is basically what I'm trying to get at.

(Note to future readers: the answer here got me started, but is not the final solution in of itself. You will have to dig into the Moose::Meta::TypeConstraint classes to actually do what I'm looking for here.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

注定孤独终老 2024-08-23 13:24:14

不确定我是否遵循您的要求,也许强制可能会做您想要的事情?

但是要获取属性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/

Not sure I follow what you are after and perhaps Coercions might do what you want?

However to get the attributes 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/

稀香 2024-08-23 13:24:14

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

Out of curiosity why not use/extend MooseX::Storage? It does Serialization, and has for about two and a half years. At the very least MooseX::Storage will help you by showing how a (well tested and production ready) serialization engine for Moose is written.

深海夜未眠 2024-08-23 13:24:14

我不太确定我理解(也许您可以包含一些伪代码来演示您正在寻找的内容),但听起来您可以通过定义新的属性特征来获得您想要的行为:设置您的属性,以便类上的一堆方法委托给属性的对象(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< /a> 对象,您可以对其进行诸如 is_a_type_of() 之类的调用。

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

I'm not quite sure I understand (perhaps you can include some pseudocode that demonstrates what you are looking for), but it sounds like you could possibly get the behaviour you want by defining a new attribute trait: set up your attribute so that a bunch of methods on the class delegate to the attribute's object (isa => 'MySerializer', handles => [ qw(methods) ]).

You might possibly also need to subclass Moose::Meta::Class (or better, add a role to it) which augments the behaviour of add_attribute().

Edit: If you look at the source for Moose::Meta::Attribute (specifically the _process_options method), you will see that the isa option is processed by Moose::Util::TypeConstraints to return the actual type to be stored in the type_constraint field in the object. This will be a Moose::Meta::TypeConstraint::Class object, which you can make calls like is_a_type_of() against.

This field is available via the type_constraint method in Moose::Meta::Attribute. See Moose::Meta::TypeConstraint for all the interfaces available to you for checking an attributes's type.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文