2011-01-17 5 views
0

私は、各フィールドに異なる検証ルールのセットを持つ一連のフィールドを持っています。perlサブルーチンリファレンス

ハッシュリファレンスを検証するためのサブルーチンリファレンスを配置しました。

現在のところ、私のコンストラクタにありますが、プライベートサブのコンストラクタから取り出したいと思っています。

私は私が私の検証を超えるいくつかのよりよい制御を持っているように、今私は、私のコンストラクタのうち、すべてこの検証ルールを取り出したいと以下のようないくつかのことをしたい

sub new { 
my $class = shift; 
my $self = {@_}; 

$class = (ref($class)) ? ref $class : $class; 
bless($self, $class); 

$self->{Validations} = { 
    Field1 => {name => sub{$self->checkField1(@_);},args => [qw(a b c)]} 
    Field2 => {name => sub{$self->checkField2(@_);},args => {key1, val1}} 
.. 
.. 
.. 
.. 
}; 

return $self; 
} 

以下のようにそれを行っていますタイプフィールドに基づいたルール。(セイいくつかのルールは、フィールドの1セットに共通していると私はフィールドの値を上書きすることにより、他のルールのためのルールを上書きすることができます。)

bless($self, $class); 

    $self->{Validations} = $self->_getValidation($self->{type}); 

    return $self; 
} 
sub _getValidation{ 
    my ($self,$type) = @_; 
    my $validation = { 
    Field1 => {name => sub {$self->checkField1(@_);}, args => {key1 => val1}},}; 

    return $validation; 
} 

しかし、誰もが私に言うことができます私はCan't use string ("") as a subroutine ref while "strict refs" in use at...を取得していますなぜこの動作がsub ref。私が私の名前のキーをチェックすると、nullまたはサブ{ダミー}になる。

+0

もう一つ、私はそれを行っています'このようにして、個々のフィールドも検証しなければならず、' $ obj-> checkField1のような個々のフィールドルールを呼び出すことも考えなければなりません( 'string'、@ args);もし私のアプローチを変更する準備が整ったら – awake416

+0

ここで面白いことが起きましたが、問題なく動作します。実際にField1を開始するキーを定義し、 (..しかし、この質問は、よりよいアプローチのためにまだ開いている。 – awake416

+0

バリデーションは属性に入れているオブジェクトごとに実際に違うのですか?それとも、 '$ self'をキャプチャするだけですか?あなたがする必要はありませんので... – hobbs

答えて

5

Mooseの再現性が低いと思われます。同様のものを構築するのではなくMooseを使用することを検討してください。

エラーメッセージは、コードでコード参照が必要な場所に文字列を渡すことを意味します。スタックトレースを取得して、エラーの原因を特定します。

Carp :: Alwaysを使用して、$SIG{__DIE__}ハンドラをオーバーライドしてスタックトレースを生成するか、コードにCarp::confessを挿入することで、これを行うことができます。

はここで、それはあなたのモジュールの初期化前に実行されますあなたのコードでこれをスティック、sigdieソリューションです:

$SIG{__DIE__} = sub { Carp::confess(@_) }; 

あなたはBEGINブロックに入れる必要があるかもしれません。

私は実際にこのアプローチをオブジェクトの構築に使用することをお勧めしません。あなたは、あなたのオブジェクトの一部としてコンストラクタに渡された任意のランダムな駄目をうれしく祝福します!あなたは穏やかにあなたの物の内部に手を伸ばす。フィールド検証規則* ではなく、がコンストラクタに属します。これらは属性ミューテータに属します。

あなたのプラクティスをクリーンアップ、DIYオブジェクトを使用する必要がある場合:

# Here's a bunch of validators. 
# I set them up so that each attribute supports: 
# Multiple validators per attribute 
# Distinct error message per attribute 
my %VALIDATORS = (

    some_attribute => [ 
     [ sub { 'foo1' }, 'Foo 1 is bad thing' ], 
     [ sub { 'foo2' }, 'Foo 2 is bad thing' ], 
     [ sub { 'foo3' }, 'Foo 3 is bad thing' ], 
    ], 
    other_attribute => [ [ sub { 'bar' }, 'Bar is bad thing' ] ], 

); 


sub new { 
    my $class = shift; # Get the invocant 
    my %args = @_;  # Get named arguments 

    # Do NOT make this a clone method as well 

    my $self = {}; 
    bless $class, $self; 

    # Initialize the object; 
    for my $arg (keys %args) { 

     # Make sure we have a sane error message on a bad argument. 
     croak "Bogus argument $arg not allowed in $class\n" 
      unless $class->can($arg); 

     $self->$arg($args{$arg}); 
    } 

    return $self; 
} 

# Here's an example getter/setter method combined in one. 
# You may prefer to separate get and set behavior. 

sub some_attribute { 
    my $self = shift; 

    if(@_){ 
     my $val = shift; 

     # Do any validation for the field 
     $_->[0]->($val) or croak $_->[1] 
      for @{ $VALIDATORS{some_attribute} || [] }; 

     $self->{some_attribute} = $val; 
    } 

    return $self->{some_attribute}; 

} 

すべてこのコードは非常にいいですが、あなたはすべての属性のためのあなたの属性コードを繰り返す必要があります。これは、エラーが発生しやすい定型コードの多くを意味します。クロージャーや文字列evalを使ってメソッドを動的に作成する方法や、Class :: Accessor、Class :: Struct、Accessor :: TinyなどのPerlの多くのクラス生成ライブラリの1つを使用してこの問題を回避することができます。

あなたは[ムース] [3]を学ぶことができます。 MooseはPerl OOPの習慣を引き継いできた新しい(ish)オブジェクトライブラリです。これは、機能の強力なセットを提供し、劇的に、古典的なPerlのOOPを超える定型を削減:

use Moose; 

type 'Foo' 
    => as 'Int' 
    => where { 
     $_ > 23 and $_ < 42 
    } 
    => message 'Monkeys flew out my butt'; 

has 'some_attribute' => (
    is => 'rw', 
    isa => 'Foo', 
); 
+0

daotoadありがとう、私はムーサーを使いたいと思っているが、私はこのプロジェクトでは、いくつかの厄介な避けられない制約のために使うことはできない:-(... – awake416

2

私はあなたが持っていたすべてのものを読んでいないが、これが私を襲った:

sub new { 
    my $class = shift; 
    my $self = {@_}; 

    $class = (ref($class)) ? ref $class : $class; 
    bless($self, $class); 

通常、あなたが作成します新しいオブジェクトの場合、ユーザーは$selfをオブジェクトの1つとして渡しません。それがあなたが作成しているものです。

sub new { 
    my $class = shift; #Contains the class 
    my %params = @_;  #What other parameters used 

    my $self = {};  #You're creating the $self object as a reference to something 
    foreach my $param (keys (%params)) { 
     $self->{$param} = $params{$param}; 
    } 

    bless ($self, $class) #Class is provided. You don't have to check for it. 
    return $self #This is the object you created. 
} 

さて、$selfは、上記の例のようにハッシュへの参照である必要はありません。

は、通常はこのような何かを参照してください。それは配列への参照かもしれません。あるいは関数にすることもできます。しかし、通常は参考になります。あなたのnewサブルーチンによって作成されているので、主なポイントは、ユーザーが$selfを渡さないということです。

また、newサブルーチンが呼び出されたときに指定されているので、$classの値をチェックする必要はありません。

あなたは(方法によって優れたアイデア、)プライベートクラスであなたの検証を行いたい場合は、bless後にそうすることができます:ここに追加する

sub new { 
    my $class = shift; #Contains the class 
    my %params = @_;  #What other parameters used 

    my $self = {};  #You're creating the $self object as a reference to something 
    foreach my $param (keys (%params)) { 
     $self->{$param} = $params{$param}; 
    } 

    bless ($self, $class) #Class is provided. You don't have to check for it. 

    #Now you can run your verifications since you've blessed the object created 
    if (not $self->_validate_parameters()) { 
     croak qq(Invalid parameters passed in class $class); 
    } 
    return $self #This is the object you created. 
} 
+0

私を訂正してくれてありがとう。 1つのパラメータタイプに基づいています。id型はladiesです、私は別のバリデーションルールを使用したいと思います。 – awake416