2014-03-25 39 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