File-Marker
view release on metacpan or search on metacpan
lib/File/Marker.pm view on Meta::CPAN
use 5.006;
use strict;
use warnings;
package File::Marker;
# ABSTRACT: Set and jump between named position markers on a filehandle
our $VERSION = '0.14'; # VERSION
our @ISA = qw( IO::File );
use Carp;
use IO::File;
use Scalar::Util 1.09 qw( refaddr weaken );
#--------------------------------------------------------------------------#
# Inside-out data storage
#--------------------------------------------------------------------------#
my %MARKS = ();
# Track objects for thread-safety
my %REGISTRY = ();
#--------------------------------------------------------------------------#
# new()
#--------------------------------------------------------------------------#
sub new {
my $class = shift;
my $self = IO::File->new();
bless $self, $class;
weaken( $REGISTRY{ refaddr $self } = $self );
$self->open(@_) if @_;
return $self;
}
#--------------------------------------------------------------------------#
# open()
#--------------------------------------------------------------------------#
sub open {
my $self = shift;
$MARKS{ refaddr $self } = {};
$self->SUPER::open(@_);
$MARKS{ refaddr $self }{'LAST'} = $self->getpos;
return 1;
}
#--------------------------------------------------------------------------#
# set_marker()
#--------------------------------------------------------------------------#
sub set_marker {
my ( $self, $mark ) = @_;
croak "Can't set marker on closed filehandle"
if !$self->opened;
croak "Can't set special marker 'LAST'"
if $mark eq 'LAST';
my $position = $self->getpos;
croak "Couldn't set marker '$mark': couldn't locate position in file"
if !defined $position;
$MARKS{ refaddr $self }{$mark} = $self->getpos;
return 1;
}
#--------------------------------------------------------------------------#
# goto_marker()
#--------------------------------------------------------------------------#
sub goto_marker {
my ( $self, $mark ) = @_;
croak "Can't goto marker on closed filehandle"
if !$self->opened;
croak "Unknown file marker '$mark'"
if !exists $MARKS{ refaddr $self}{$mark};
my $old_position = $self->getpos; # save for LAST
my $rc = $self->setpos( $MARKS{ refaddr $self }{$mark} );
croak "Couldn't goto marker '$mark': could not seek to location in file"
if !defined $rc;
$MARKS{ refaddr $self }{'LAST'} = $old_position;
lib/File/Marker.pm view on Meta::CPAN
my ( $self, $filename ) = @_;
my $outfile = IO::File->new( $filename, "w" )
or croak "Couldn't open $filename for writing";
my $markers = $MARKS{ refaddr $self };
for my $mark ( keys %$markers ) {
next if $mark eq 'LAST';
print $outfile "$mark\n";
print $outfile unpack( "H*", $markers->{$mark} ), "\n";
}
close $outfile;
}
#--------------------------------------------------------------------------#
# load_markers()
#--------------------------------------------------------------------------#
sub load_markers {
my ( $self, $filename ) = @_;
my $infile = IO::File->new( $filename, "r" )
or croak "Couldn't open $filename for reading";
my $markers = $MARKS{ refaddr $self };
my $mark;
while ( defined( $mark = <$infile> ) ) {
chomp $mark;
my $position = <$infile>;
chomp $position;
$position = pack( "H*", $position );
$markers->{$mark} = $position;
}
close $infile;
}
#--------------------------------------------------------------------------#
# DESTROY()
#--------------------------------------------------------------------------#
sub DESTROY {
my $self = shift;
delete $MARKS{ refaddr $self };
delete $REGISTRY{ refaddr $self };
$self->SUPER::DESTROY;
}
#--------------------------------------------------------------------------#
# CLONE()
#--------------------------------------------------------------------------#
sub CLONE {
for my $old_id ( keys %REGISTRY ) {
# look under old_id to find the new, cloned reference
my $object = $REGISTRY{$old_id};
my $new_id = refaddr $object;
# relocate data
$MARKS{$new_id} = $MARKS{$old_id};
delete $MARKS{$old_id};
# update the weak reference to the new, cloned object
weaken( $REGISTRY{$new_id} = $object );
delete $REGISTRY{$old_id};
}
return;
}
#--------------------------------------------------------------------------#
# _object_count() -- used in test scripts to see if memory is leaking
#--------------------------------------------------------------------------#
sub _object_count {
return scalar keys %REGISTRY;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
File::Marker - Set and jump between named position markers on a filehandle
=head1 VERSION
version 0.14
=head1 SYNOPSIS
use File::Marker;
my $fh = File::Marker->new( 'datafile.txt' );
my $line1 = <$fh>; # read a line
$fh->set_marker( 'line2' ); # mark the current position
my @rest = <$fh>; # slurp the remainder
$fh->goto_marker( 'line2' ); # jump back to the marked position
my $line2 = <$fh>; # read another line
=head1 DESCRIPTION
File::Marker allows users to set named markers for the current position in
a filehandle and then later jump back to a marked position. A
File::Marker object is a subclass of L<IO::File>, providing full filehandle
object functionality.
File::Marker automatically sets a special, reserved marker, 'LAST', when it
jumps to a marker, allowing an easy return to a former position.
This module was written as a demonstration of the inside-out object technique
for the NY Perl Seminar group. It is intended for teaching purposes and has
not been tested in production code.
=head1 USAGE
=head2 new
( run in 2.282 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )