What should I do if a Moose builder method fails?

前端 未结 3 1234
闹比i
闹比i 2021-02-14 08:46

What is the best way to handle a failure in a builder method?

For example:

package MyObj;
use Moose;
use IO::File;

has => \'file_name\'   ( is =>          


        
3条回答
  •  一向
    一向 (楼主)
    2021-02-14 09:24

    Is there a way to signal that the builder failed, and the attribute should remain cleared?

    No. This wouldn't make sense, the builder will fire if the attribute is cleaered, if it was cleared within the builder it would just fire when you made the next call to it, and remain in the cleared state. A waste of a lot work, just to set-something-if-it-works-and-if-not-continue.

    The type-union suggestion is a good one but then you have to write code that can function with two radically different cases: a filehandle, and a non-existant file handle. This seems like a poor idea.

    If the file-handle isn't essential to the task then it probably isn't shared amongst the same scope of things with access to the object. If this is the case then the object can just provide a method that generates a filehandle from the object. I do this in production code. Don't get hellbent on making everything a lazy-attribute, some things are functions of attribute and it doesn't always make sense to attach them to the object.

    sub get_fh {                                                                
      my $self = shift;                                                         
    
      my $abs_loc = $self->abs_loc;                                             
    
      if ( !(-e $abs_loc) || -e -z $abs_loc ) {                                 
        $self->error({ msg => "Critical doesn't exist or is totally empty" });  
        die "Will not run this, see above error\n";                             
      }                                                                         
    
      my $st = File::stat::stat($abs_loc);                                      
      $self->report_datetime( DateTime->from_epoch( epoch => $st->mtime ) );    
    
      my $fh = IO::File->new( $abs_loc, 'r' )                                   
        || die "Can not open $abs_loc : $!\n"                                   
      ;                                                                         
    
      $fh;                                                                      
    
    }                                                                           
    

    A totally different approach is to subclass IO::File, with the meta data about the file you want to keep. Sometimes this is effective, and a great solution:

    package DM::IO::File::InsideOut;
    use feature ':5.10';
    use strict;
    use warnings;
    
    use base 'IO::File';
    
    my %data;
    
    sub previouslyCreated {
      $data{+shift}->{existed_when_opened}
    }
    
    sub originalLoc {
      $data{+shift}->{original_location}
    }
    
    sub new {
      my ( $class, @args ) = @_;
    
      my $exists = -e $args[0] ? 1 : 0;
    
      my $self = $class->SUPER::new( @args );
    
      $data{$self} = {
        existed_when_opened => $exists
        , original_location => $args[0]
      };
    
      $self;
    
    };
    

提交回复
热议问题