フレームワーク (6) - lvalue で validation もしたい

フレームワーク (6) - lvalue で validation もしたい #

header_type というメソッドを実装していた時のこと。

 package CGI::Minimal::App;
 sub header_type :lvalue {
   my $self = shift;
   $self->{__HEADER_TYPE};
 }

これで

  • $self->header_type(‘redirect’);
  • $self->header_type = ‘redirect’;

どちらの書き方でも setter として動作し、$self->header_type は getter にもなる。が、header_type に set できる値は制限したいので、

 package CGI::Minimal::App;
 sub header_type :lvalue {
   my $self = shift;
   my $type = shift;
 
   die 'Invalid header type: '.$type
     unless ( $type eq 'header' or $type eq 'redirect' );
 
   $self->{__HEADER_TYPE} = $type;
 
   $self->{__HEADER_TYPE};
 }

というようなコードを書くと、

$self->header_type(‘dummy’);

はエラーになるが

$self->header_type = ‘dummy’;

はエラーにならない

lvalue の時も validation のロジックを通したいので、しばし思案。結果、tie を使うことにした。動いてるけど。。。もっとスマートな実装方法はないものか

 package CGI::Minimal::App;
 sub header_type :lvalue {
   my $self = shift;
   my $header_type = shift;
 
   # First use? Create new __HEADER_TYPE!
   $self->{__HEADER_TYPE} = 'header' unless exists $self->{__HEADER_TYPE};
 
   # tie getter and setter to CGI::Minima::App::Magic (to validate)
   tie $self->{__HEADER_TYPE}, 'CGI::Minimal::App::Magic', $self,
     sub {
       my $self = shift;
       $self->{__HEADER_TYPE};
     },
     sub {
       my $self = shift;
       my $value = shift;
 
       # check allowed header
       die 'Invalid header type: '.$value
         unless ( grep {$_ eq lc($value)} ('header', 'redirect', 'not_found', 'not_modified', 'none') );
       $self->{__HEADER_TYPE} = lc($value);
     };
 
   $self->{__HEADER_TYPE} = $header_type if defined $header_type;
   $self->{__HEADER_TYPE};
 }
 1;
 package CGI::Minimal::App::Magic;
 sub TIESCALAR {
   my $class = shift;
   my $self = shift;
   my $getter = shift;
   my $setter = shift;
 
   $class = ref $class || $class || __PACKAGE__;
   bless({obj => $self, getter => $getter, setter => $setter}, $class);
 }
 
 sub FETCH {
   my $self = shift;
   $self->{getter}->($self->{obj});
 }
 
 sub STORE {
   my $self = shift;
   my $value = shift;
 
   $self->{setter}->($self->{obj}, $value);
 }
 1;

See Also

Copyright © 髭。/ Hugo + hugo-book