2014-03-25 38 views
2

我正在处理一些非Moose遗留代码,我想用Moose类对其进行扩展。这是遗留代码的简化:Moose - 修改对象散列中的默认属性位置

package My::Legacy; 

sub create { 
    my ($class, $args) = @_; 

    my $fields = { _fields => {}}; 
    foreach my $key (keys %$args) { 
    $fields->{_fields}->{$key} = $args->{$key} 
    } 
    bless $fields, $class; 
} 

1; 

我::遗产类处理所有的CRUD操作,缓存和其他的东西。所有操作都是根据内部_field散列中包含的值执行的,因此,例如,如果要更新值,它必须位于_field散列中。 My :: Legacy类为此提供setter/getter。

我::遗产是需要由其提供的“糖”几类子类:我::遗产::对象A我::遗产::对象B

我需要再添加一个,我想用Moose扩展它。问题是,每次我都会设置一个属性的时候,我将不得不继续其价值同步在内部_fields散,因此,例如,如果我有...

package My::Legacy::MyMooseObj; 

use Moose; 
use MooseX::NonMoose; 
use namespace::autoclean; 

has _fields => (
    isa   => HashRef, 
    is   => 'rw', 
    default  => sub { {} }, 
); 

has attr_a => (
    isa => 'Int', 
    is => 'ro', 
); 

has attr_b => (
    isa => 'Str', 
    is => 'ro', 
); 


__PACKAGE__->meta->make_immutable; 

...我这样做:

my $MyMooseObj = My::Legacy::MyMooseObj->new(); 
$MyMooseObj->attr_a(15); 

...我想attr_a的_fields被设置为好,所以如果我dump出来的话就会像:

bless({ 
      '_fields' => { 
          'attr_a' => 15, 
         }, 
      'attr_a' => 15, 
      }, 'My::Legacy::MyMooseObj'); 

的方式我拿出实现这一目标是为了增加一个触发器,每个属性写它的价值在_fields哈希每次设置:

 has attr_b => (
     isa => 'Str', 
     is => 'ro', 
     trigger => sub { # Write in the _fields attribute attr_b value! }, 
    ); 

这是一个有点恼人,因为每一次我添加了一个新的属性,我必须确保它有触发器设置:/

你能想到更好的方法吗?有没有办法告诉Moose在默认情况下读取/写入不在对象哈希的“根目录”中的属性(所以在我的情况下,从_fields)读取/写入属性?

回答

1

这或多或少你想要做什么......

use strict; 
use warnings; 

{ 
    package My::Legacy::MyMooseObj; 

    use Moose; 
    use MooseX::FunkyAttributes; 
    use namespace::autoclean; 

    has _fields => (
     isa   => 'HashRef', 
     is   => 'rw', 
     default  => sub { {} }, 
     lazy  => 1, # you want this, for the rest to work 
    ); 

    has attr_a => (
     isa   => 'Int', 
     is   => 'ro', 
     traits  => [ FunkyAttribute ], 
     custom_get => sub { $_->_fields->{attr_a} }, 
     custom_set => sub { $_->_fields->{attr_a} = $_[-1] }, 
     custom_has => sub { exists($_->_fields->{attr_a}) }, 
    ); 

    has attr_b => (
     isa   => 'Str', 
     is   => 'rw', 
     traits  => [ FunkyAttribute ], 
     custom_get => sub { $_->_fields->{attr_b} }, 
     custom_set => sub { $_->_fields->{attr_b} = $_[-1] }, 
     custom_has => sub { exists($_->_fields->{attr_b}) }, 
    ); 
} 

my $obj = My::Legacy::MyMooseObj->new(attr_a => 42); 
$obj->attr_b(666); 

print $obj->dump; 

随着MooseX :: FunkyAttributes的当前版本中,构造函数将无法正常如果你完成整个__PACKAGE__->meta->make_immutable虽然工作。 :-(

稍微钻研更深的元编程...

use strict; 
use warnings; 

{ 
    package My::Legacy::MyMooseObj; 

    use Moose; 
    use MooseX::FunkyAttributes; 
    use namespace::autoclean; 

    has _fields => (
     isa   => 'HashRef', 
     is   => 'rw', 
     default  => sub { {} }, 
     lazy  => 1, # you want this, for the rest to work 
    ); 

    sub funky_has { 
     my ($attr, %opts) = @_; 
     has $attr => (
      is   => 'ro', 
      traits  => [ FunkyAttribute ], 
      custom_get => sub { $_->_fields->{$attr} }, 
      custom_set => sub { $_->_fields->{$attr} = $_[-1] }, 
      custom_has => sub { exists($_->_fields->{$attr}) }, 
      %opts, 
     ); 
    } 

    funky_has attr_a => (isa => 'Int'); 
    funky_has attr_b => (isa => 'Str', is => 'rw'); 
} 

my $obj = My::Legacy::MyMooseObj->new(attr_a => 42); 
$obj->attr_b(666); 

print $obj->dump; 
+0

的问题是,我不得不“特征”,“custom_get”,“custom_set”,“custom_has”添加到所有属性我想要“同步”,所以也许我最初的做法可以节省我一些打字的时间,并且我仍然可以保留__PACKAGE __-> meta-> make_immutable? – barbasa

+0

你没有想到* meta *够了!我们再添加一个例子... – tobyink

+0

哇!超级整洁!非常感谢...我明确地开始思考更多_meta_ :) – barbasa