2011-04-28 52 views
3

散列表是Perl對象的典型初始值設定項。現在你的輸入是不可靠的,因爲你不知道對於任何給定的鍵是否會有一個定義的值,也不知道密鑰是否存在。現在你想把這種不可靠的輸入提供給Moose對象,而缺少的鍵完全沒問題,你確實想擺脫未定義的值,所以你最終不會得到一個充滿未定義屬性的對象。使Moose構造函數忽略undef參數

當實例化對象並過濾出未定義的值時,您當然可以非常小心。但是讓我們假設你想在你的構造函數中安裝這個過濾器,因爲它在一個地方。你希望構造函數忽略未定義的值,但不會在遇到它們時死掉。

對於存取方法,您可以使用around左右以防止該屬性設置爲undef。但是這些method modifiers不是爲構造函數調用的,僅用於訪問器。 Moose中是否有類似的設施可以達到與c'tor相同的效果,即排除任何undef屬性被接受?

請注意,如果屬性爲undef,則Moose Any類型將在對象中創建哈希鍵。我不希望這樣,因爲我想%$self不包含任何undef值。

這裏的一些測試中,我所做的:

package Gurke; 
use Moose; 
use Data::Dumper; 

has color => is => 'rw', isa => 'Str', default => 'green'; 
has length => is => 'rw', isa => 'Num'; 
has appeal => is => 'rw', isa => 'Any'; 

around color => sub { 
    # print STDERR Dumper \@_; 
    my $orig = shift; 
    my $self = shift; 
    return $self->$orig unless @_; 
    return unless defined $_[0]; 
    return $self->$orig(@_); 
}; 

package main; 
use Test::More; 
use Test::Exception; 

my $gu = Gurke->new; 
isa_ok $gu, 'Gurke'; 
diag explain $gu; 
ok ! exists $gu->{length}, 'attribute not passed, so not set'; 
diag q(attempt to set color to undef - we don't want it to succeed); 
ok ! defined $gu->color(undef), 'returns undef'; 
is $gu->color, 'green', 'value unchanged'; 
diag q(passing undef in the constructor will make it die); 
dies_ok { Gurke->new(color => undef) } 
    'around does not work for the constructor!'; 
lives_ok { $gu = Gurke->new(appeal => undef) } 'anything goes'; 
diag explain $gu; 
diag q(... but creates the undef hash key, which is not what I want); 
done_testing; 
+0

閱讀[Moose :: Cookbook](http://search.cpan.org/perldoc/Moose::Cookbook)。特別是[Moose :: Cookbook :: Basics :: Recipe10](http://search.cpan.org/perldoc/Moose::Cookbook::Basics::Recipe10)。 – 2011-04-28 19:29:56

回答

13

這正是MooseX::UndefTolerant所做的。如果你讓你的類不可變,它會比編寫你自己的BUILDARGS方法快得多,因爲代碼被內聯到生成的構造函數中。

+0

哦,我的哦,我的,有這麼多的穆斯:: WhatNot和MooseX :: WhatElse模塊...他們做我想要的! :-)我想,這將需要更多的時間來使用玩具。感謝以太,這可以說是優於編寫自己的BUILDARGS,所以我接受這是最好的答案。 – Lumi 2011-04-28 22:09:38

+3

@Michael::)如果irc是你的東西,你可以在irc.perl.org #moose幾乎24/7全天候找到優秀的支持網絡。通常情況下,如果沒有擴展程序可以滿足你的要求,有人會扣下來爲你寫一個,只是爲了踢球! :D – Ether 2011-04-29 00:06:06

+0

謝謝!實際上從來沒有做過任何IRC,但這聽起來像是一次嘗試。我會陪伴什麼客戶?看起來'錫'是古典的選擇,可用於Cygwin。也給Windows的'mIRC'嘗試一下。 – Lumi 2011-04-29 08:56:42

5

只需提供自己的BUILDARGS子程序。

package Gurke; 

... 

around 'BUILDARGS' => sub{ 
    my($orig,$self,@params) = @_; 
    my $params; 
    if(@params == 1){ 
    ($params) = @params; 
    }else{ 
    $params = { @params }; 
    } 

    for my $key (keys %$params){ 
    delete $params->{$key} unless defined $params->{$key}; 
    } 

    $self->$orig($params); 
}; 
+0

非常好,再次感謝Brad!我的第一本能是用'each'替換'keys',但當然在迭代過程中調用'delete'時不能這樣做。非常感激! – Lumi 2011-04-28 20:26:56

1

我意識到這是一個有些重複工作,但你可以BUILDARGS勾男星:

around BUILDARGS => sub { 
    my $orig = shift; 
    my $class = shift; 
    my %params = ref $_[0] ? %{$_[0]} : @_; 

    return $class->$orig(
     map { $_ => $params{$_} } 
     grep { defined $params{$_} } 
     keys %params 
    ); 
}; 

編輯:編輯,以支持甚至傳遞到構造函數的引用。

+0

如果有人調用'Class-> new({key => value})'(即傳遞一個hashref而不是一個列表),這將不起作用。 – cjm 2011-04-28 19:51:09

+0

@cjm - 謝謝,我不知道這甚至是可能的。 – bvr 2011-04-29 05:06:50

0

儘管給出的例子闡明瞭這個問題的靈感來自於處理傳遞給構造函數的undef屬性,但這個問題本身還意味着只向構造函數傳遞undef的情況,這是我遇到並想要的東西解決。

例如,Class->new(undef)。我喜歡bvr's BUILDARGS answer。它可以擴展到處理傳遞一個民主基金價值而不是hashref作爲唯一參數構造函數的情況下:

around BUILDARGS => sub { 
    my $orig = shift; 
    my $class = shift; 
    my %params = defined $_[0] ? ref $_[0] ? %{$_[0]} : @_ :(); 

    return $class->$orig(
     map { $_ => $params{$_} } 
     grep { defined $params{$_} } 
     keys %params 
    ); 
}; 

MooseX :: UndefTolerant似乎不支持這種情況。