OzVegan has asked for the wisdom of the Perl Monks concerning the following question:

I've got a class/package called Backup_Drive. It records details of backup files from a WMI Query. I write the following to instantiate it:

$drive = Coles_Backup_Drive->new( \$disk, SPACE_THRESHOLD );

Now if I add a file reference to it calling the add_full_backup method directly: $drive->add_full_backup( $database_name, $file ); it records the file details into the Class's %_last_full_backups hash and @_full_backup_files array and when I leave the method's scope it doesn't lose the detail. I can keep adding file details.

If I add file by add_file method then the $file reference is passed to the add_full_backup method internally but once out of the latter method's scope the array and hash become empty.

I've tried sending the file as a reference of a reference. I can't take away the my clause because then use strict cracks it. I've tried $self->add_full_backup( $database_name, $file ); I don't understand why the hash and array lose their detail after add_full_backup method is called indirectly but keep the details for the life of the class if called directly.

Below is the class code:

package Backup_Drive; use strict; use constant LAST_MODIFIED => 0; use constant FILE_SIZE => 1; use constant TRANSACTION_LOG => 'trn'; use constant FULL_BACKUP => 'bak'; # make database backups private my %_last_full_backups = (); my @_full_backup_files = []; my %_trn_backups =(); my $_space_threshold; sub new{ my ( $class, @arg ) = @_; my $self = bless $arg[0], $class; $_space_threshold = $arg[1]; return $self; } sub add_file{ ### THIS IS THE PROBLEM WHEN PASSING DETAIL FROM THIS METHOD TO add_fu +ll_backup ### # add the file to the relevant file type data my( $self, $database_name, $file ) = @_; if( lc( $file->Extension ) eq FULL_BACKUP ){ add_full_backup( $self, $database_name, $file ); } else { add_trn_log( $self, $database_name, $file ); } print "wait\n"; } sub add_full_backup{ my( $self, $database_name, $file ) = @_; # capture the last backup details if( exists $_last_full_backups{ $database_name } ){ # record the file size only if the database backup is more rec +ent the the one recorded if( $_last_full_backups{ $database_name }->[ LAST_MODIFIED ] < + $file->LastModified ){ # one file I found returned an undefined value for its siz +e. So I defaulted the size to zero. $_last_full_backups{ $database_name } = [ $file->LastModif +ied, defined $file->FileSize ? $file->FileSize : 0 ]; } } else { # add new details $_last_full_backups{ $database_name } = [ $file->LastModified, + defined $file->FileSize ? $file->FileSize : 0 ]; } # add file to file list push ( @_full_backup_files, $file ); print "test\n"; } sub add_trn_log{ my( $self, $database_name, $file ) = @_; if( exists $_trn_backups{ $database_name } ){ $_trn_backups{ $database_name } = [ \$file ]; } else { push( @{ $_trn_backups{ $database_name } }, \$file ); } } sub get_last_backup_modified{ # return the last modified date for the passed in database my( $self, $database_name ) = @_; $_last_full_backups{ $database_name }->[ LAST_MODIFIED ]; } sub get_last_backup_file_size{ # return the last modified date for the passed in database my( $self, $database_name ) = @_; $_last_full_backups{ $database_name }->[ FILE_SIZE ]; } sub need_drive_space{ # return whether the drive needs space to keep it above the space +warning threshold. my $self = shift; if( _database_space_required() > $$self->FreeSpace ){ return 1; } return 0; } sub _database_space_required{ my $self = shift; my $total = 0; my $key; for $key ( keys %_last_full_backups ){ $total += get_last_backup_file_size( $key ); } $total += $_space_threshold; # keep above threshold warning siz +e return $total; } #__END__ 1;

Replies are listed 'Best First'.
Re: Is it a reference issue or what?
by ikegami (Patriarch) on Jan 28, 2010 at 05:56 UTC

    I can't make heads or tail of your design. What are you trying to do?

    You pass to the constructor a value that shared by all objects of that class. That makes no sense.

    The class is called Coles_Backup_Drive, but object of that class in no way represent anything of the kind. Or anything else. The objects only has one member ($$self), and it's never used.

    Are the methods suppose to be static methods aka class methods? Everything is in in static attributes aka class attributes. If so, why do you create an object?

      Yeah well, it's a work in progress. The class actually instantiates a Win32_Drive object. The global hash and array I will fix later to make them private. $$self->FreeSpace is used. It is a method of the Win32_Drive class that is blessed into the class

      So what I'm trying to do is bless the Win32_Drive class returned by a WMI query so I can add other methods to it.

      The CIM_Datafile collection ($file returned by WMI) is supposed to be of various .bak and .trn files found in that drive.

      Because the .trn files are on different drives to the .bak files I want multiple Backup_Drives that are holding their own collection of files. Then I can work out the calculations where they have to cross reference each other for information later.

      It's complicated.

        I doubt that reblessing a Win32::OLE object will work as you intend it to, because Win32::OLE objects live mainly in C/XS space. I recommend you aggregate the WMI object instead of trying to extend it, and delegate all unknown methods to the WMI object via AUTOLOAD:

        sub AUTOLOAD { my $self = shift; (my $meth = $AUTOLOAD) =~ s/.*::/; # Now call the method on the WMI object $self->{wmi}->$meth(@_); }

        The above method could be refined further to play with the stack so that all error messages point to the real caller instead of the reflection method, but for a first stab, this approach is OK.