[Melbourne-pm] IO::File with Capture for Quota

Scott Penrose scottp at linux.dd.com.au
Wed Oct 15 20:29:50 PDT 2008


Hey Guys

I am writing a module at the moment to capture IO::File open to allow me to work out the difference in file size at the start and end of an operation.

My code basically works, but it is a huge hack, and requires a hack on the call to make it work.

Firstly let me show you how I would expect the code to work:
(NOTE: This is not thread safe, it is demonstration code, not final)

  #!/usr/bin/perl
  use IO::File::Quota;
  
  # Set the call back - just print the new size
  $IO::File::Quota::quotaaction = sub { print "New size = ", @_, "\n" };
  
  # Open a file for append
  my $fh = new IO::File::Quota ">> /tmp/testfile1";
  
  # Print to the file
  print $fh "This is a new line\n";
  
  # Three methods of closing a file
  $fh->close;
  #close $fh;
  #undef $fh;

Above is code that works, but only if you use $fh->close before the object is destroyed. To get around this I added in a tiny hack to keep the file name.

  my $fh = new IO::File::Quota ">> /tmp/testfile1";
  $IO::File::Quota::filecache{$fh} = "/tmp/testfile1";

Now all three close methods work - $fh->close; close $fh; undef $fh - and in any order.

So before my questions, here is the actual module:

package IO::File::Quota;
use base qw/IO::File/;

# Keep a local copy of the size at the start
our %sizecache = ();
# Keep a local copy of the filename at the start
our %namecache = ();
# Callback function
our $quotaaction;

# Capture current size (maybe zero if not used)
sub open {
	my ($self, @rest) = @_;
	print STDERR "Open: " . $rest[0] . "\n";
	my $ret = $self->SUPER::open(@rest);
	$sizecache{$ret} = -s $ret;
	return $ret;
}

sub close {
	my ($self, @rest) = @_;
	print STDERR "Close\n";
	my $new = -s $self;
	if ($sizecache{$self} != $new && ref($quotaaction)) {
		$quotaaction->($new - $sizecache{$self});
	}
	delete $sizecache{$self};
	delete $filecache{$self};
	return $self->SUPER::close(@rest);
}

sub DESTROY {
	my $self = shift;
	my $new = -s $self;
	if (!defined($new) && exists($filecache{$self})) {
		$new = -s $filecache{$self};
	}
	print STDERR "DESTROY - $new\n";
	if (exists($sizecache{$self}) && $sizecache{$self} != $new && ref($quotaaction)) {
		$quotaaction->($new - $sizecache{$self});
	}
	delete $sizecache{$self};
	delete $filecache{$self};
}

1;

Note that close and DESTROY are partially duplicates for the moment. I would probably complete the work by using a separate method based on the filename (after sync) called from close & destroy. The reason close is important is that it may be used while the object still exists and we want to know as close to closing time as possible.

ALSO NOTE - this captures the wrong size using "-s" as it is before the file is flushed - I can fix those problems easily later.

Right down to my questions:

1) Is there anyway of getting from a file handle:
    - That it is a file on disk, rather than Socket or Pipe
    - The filename of that file

2) What else would you change - in functions, calls and purpose to make this a useful CPAN module

3) Is there already something like this you know of?

I feel like I am going about this all wrong, but I am a little stuck and in need of help.

Thanks

Scott


More information about the Melbourne-pm mailing list